/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ImagenesDBGad.Controladores;

import ImagenesDBGad.IO.Imagen;
import ImagenesDBGad.IO.ImagenConsulta;
import Utils.ArchUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 *
 * @author NiCo
 * http://code.google.com/p/imagenesdb-gad/source/browse/
 */
public class ControladorPersistencia {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        ControladorPersistencia.getInstance().getEntity(); //Crean las entidades base en la DB
        ConexionPostgreSQL.cargarScripDB();//Carga el SQL de las tablas y funciones faltantes
    }

    private ControladorPersistencia() {
    }

    public static ControladorPersistencia getInstance() {
        return ControladorPersistenciaHolder.INSTANCE;
    }

    private static class ControladorPersistenciaHolder {

        private static final ControladorPersistencia INSTANCE = new ControladorPersistencia();
        private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("ImagenesDB-GADPU");
        private static final EntityManager em = emf.createEntityManager();
    }

    public EntityManager getEntity() {
        return ControladorPersistenciaHolder.em;
    }

    public void cerrarEntity() {
        ControladorPersistenciaHolder.em.close();
    }

    /**
     * @TODO no tengo idea de que por que no puede ejecutar el SQL!!!!!!!
     */
    public void cargarScriptDB() {
        Object obj = null;
        getEntity().getTransaction().begin();
        try {
            obj = getEntity().createNativeQuery("SELECT id FROM imagen LIMIT 1").getSingleResult();
            getEntity().getTransaction().commit();
        } catch (Exception e) {
            getEntity().getTransaction().rollback();
        }
        if (obj == null) {
            /////////
            //OPCION 1 PARA CARGAR EL SCRIPT
            /////////
//            String sql = ArchUtils.getXML("src/SQLPostGis/SQLscriptDB-Gad.sql");
//            sql = sql.replaceAll("\"", "");
//            getEntity().getTransaction().begin();
//            try {
//                getEntity().createNativeQuery(sql).executeUpdate();
//                getEntity().getTransaction().commit();
//            } catch (Exception f) {
//                getEntity().getTransaction().rollback();
//            }
            /////////
            //OPCION 2 PARA CARGAR EL SCRIPT
            /////////
//            ConexionPostgreSQL.cargarScripDB();
        }
    }

    /**
     * guarda cualquier cosa que le mandes a la base de datos, le pasas el
     * objeto que quieras y lo mete, creo que tambien sirve para actualizar pero
     * el objeto que le mandas en ningun momento tendrias que haber hecho un new
     * porque sino piensa que es un objeto nuevo, igual hay un metodo para
     * actualizar mas abajo que es mas seguro por las dudas que metamos la pata
     * en algun lado del codigo
     *
     * @param object
     */
    public void guardar(Object object) {
        getEntity().getTransaction().begin();
        try {
            getEntity().persist(object);
            getEntity().getTransaction().commit();

            if (object instanceof Imagen) {
                actualizarDatos();
            }
        } catch (Exception e) {
            getEntity().getTransaction().rollback();
        }
    }

    /**
     * Dispara el Trigger para actualizar el indice
     */
    public void actualizarDatos() {
        getEntity().getTransaction().begin();
        try {
            Object obj = getEntity().createNativeQuery("SELECT gad_actualizar_datos()").getSingleResult();
            getEntity().getTransaction().commit();
        } catch (Exception e) {
            getEntity().getTransaction().rollback();
        }
    }

    /**
     * Retorna idImagen bigint, nombreImagen text, codxmlImagen text, cantObjeto
     * integer, distancia real
     *
     * @param imgPrincipal
     * @return
     */
    public ArrayList<Object[]> buscarImagenes(Imagen imgPrincipal) {
        ImagenConsulta imgConsulta = new ImagenConsulta(imgPrincipal);
        ControladorPersistencia.getInstance().guardar(imgConsulta);

        long miliSegInicio = System.currentTimeMillis();
        ArrayList<Object[]> listaResultado = new ArrayList<Object[]>();
        getEntity().getTransaction().begin();
        try {
            listaResultado = new ArrayList<Object[]>(getEntity().createNativeQuery(""
                    + " SELECT * "
                    + " FROM gad_buscar_imagen() "
                    + "     AS (id bigint, nombre text, codxml text, cantobjeto integer, distancia real)").getResultList());
            getEntity().getTransaction().commit();
        } catch (Exception e) {
            getEntity().getTransaction().rollback();
        }
        long resp = System.currentTimeMillis() - miliSegInicio;
        getEntity().getTransaction().begin();
        try {
            getEntity().createNativeQuery("INSERT INTO tiempo VALUES (" + resp + ")").executeUpdate();
            getEntity().getTransaction().commit();
        } catch (Exception e) {
            getEntity().getTransaction().rollback();
        }
        return listaResultado;
    }

    /**
     * Retorna las propiedades de la base de datos: CantImagenes, CantPivotes,
     * PromedioTiempo
     *
     * @return
     */
    public Object[] getDatosDB() {
        Object[] prop = null;
        getEntity().getTransaction().begin();
        try {
            prop = (Object[]) getEntity().createNativeQuery(""
                    + " SELECT * "
                    + " FROM gad_datos()").getSingleResult();
            getEntity().getTransaction().commit();
        } catch (Exception e) {
            getEntity().getTransaction().rollback();
        }
        return prop;
    }

    private static class ConexionPostgreSQL {

        private static final Connection connectionFactory;
        private static String DRIVER_DB = "org.postgresql.Driver";
        private static String URL_DB = "jdbc:postgresql://localhost:5432/ImagenesDB-GAD";
        private static String USUARIO_DB = "postgres";
        private static String CLAVE_DB = "postgres";

        static {
            try {
                connectionFactory = ConexionPostgreSQL.getConexion();
            } catch (Throwable ex) {
                System.err.println("Initial SessionFactory creation failed." + ex);
                throw new ExceptionInInitializerError(ex);
            }
        }

        private static void runTest() {
            System.out.println("Chequeando si el DRIVER esta cargado.");
            try {
                Class.forName(DRIVER_DB);
            } catch (ClassNotFoundException cnfe) {
                System.out.println("No se ha podido encontrar el DRIVER! Compruebe que se encuantra cargada la libreria");
                System.out.println("Listado de stack trace y fin de la conexion.");
                System.exit(1);
            }
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(URL_DB, USUARIO_DB, CLAVE_DB);
                conn = ConexionPostgreSQL.getConexion();
            } catch (SQLException se) {
                System.out.println("No se ha podido hacer la conexion: Usuario, Contraseña o URL invalidos.");
                System.exit(1);
            }
            if (conn != null) {
                System.out.println("Conexion a la DB EXITOSA!");
                try {
                    ConexionPostgreSQL.cerrarConexion();
                    System.out.println("Conexion cerrada exitosamente");
                } catch (SQLException ex) {
                    System.out.println("No fue posible cerrar la conexion.");
                    System.exit(1);
                }
            }
        }

        /**
         * El siguiente metodo obtiene y devuelve una nueva conexion.
         *
         * @return Connection, corresponde a la conexion obtenida.
         * @throws java.sql.SQLException
         * @author ivanpianetti
         */
        private static Connection getConexion() throws SQLException {
            try {
                Class.forName(DRIVER_DB);
            } catch (ClassNotFoundException ex) {
            }
            Connection conne = DriverManager.getConnection(URL_DB, USUARIO_DB, CLAVE_DB);
            return conne;
        }

        public static Connection getConnectionFactory() {
            return connectionFactory;
        }

        /**
         * El metodo a continuacion cierra una determinada conexion.
         *
         * @param conn tipo Connection, corresponde a la conexion a cerrar.
         * @throws java.sql.SQLException
         * @author ivanpianetti
         */
        public static void cerrarConexion() throws SQLException {
            connectionFactory.close();
        }

        /**
         * OPCIÓN 2 PARA CARGAR EL SCRIPT DE DATOS
         */
        private static void cargarScripDB() {
            String sql = ArchUtils.getXML("src/SQLPostGis/SQLscriptDB-Gad.sql");
            sql = sql.replaceAll("\"", "");
            Connection conn = ConexionPostgreSQL.getConnectionFactory();
            try {
                conn.createStatement();
                PreparedStatement prepST = conn.prepareStatement(sql);
                prepST.executeUpdate();
                conn.commit();
            } catch (SQLException ex) {
                try {
                    conn.rollback();
                } catch (SQLException ex1) {
                }
            } finally {
                try {
                    ConexionPostgreSQL.cerrarConexion();
                } catch (SQLException ex) {
                }
            }
        }
    }
}
